/****************************************************************************
* Alberto De Pena
* qotd.cpp
*
* Due: March 7, 2011
*****************************************************************************/
#ifndef WINSOCK2
#define WINSOCK2
#include <winsock2.h>
#pragma comment ( lib, "ws2_32.lib" )
#endif

#ifndef IOSTREAM
#define IOSTREAM
#include <iostream>
#include <fstream>
#endif

#include <time.h>
#include <string.h>
#include <string>
using namespace std;

const int  MAX_HOSTNAME  = 1024;
const int  MAX_PORT      = 128;
const int  MAX_RECV_BUF  = 2048;
const int  MAX_SEND_BUF  = 128;
const int  WAIT_TIME = 15;
const int TIMEBUF_SIZE = 26;
#define MAXIPBUF  20



/****************************************************************************
* Function declarations
****************************************************************************/
void chomp( char* );

char* timestamp();
char* GetHostIP();

int wait( SOCKET, int, int );

SOCKET passivesock( char*, char* );

/*****************************************************************************
*   chomp - used for the timestamp function
*****************************************************************************/
void chomp( char* s) 
{   
    int i = 0;
    while ( s[i] != '\0' )
    {
        i++;
    }               
    if ( s[i-1] == '\n' )
    {
        s[i-1] = '\0';
    }   
}

/*****************************************************************************
*   timestamp - string containing the immediate date
*****************************************************************************/
char* timestamp()
{   
    static char time_s[TIMEBUF_SIZE];
    time_t ltime;                           

    time( &ltime );                         
    ctime_s( time_s, TIMEBUF_SIZE, &ltime );    
    chomp( time_s );

    return time_s;
}

/*****************************************************************************
* GetHostIP - returns teh Host IP address in decimal dotted form
*****************************************************************************/
char* GetHostIP()
{
    char szLclHost [MAX_HOSTNAME];
    LPHOSTENT lpstHostent;
    SOCKADDR_IN stLclAddr;
    SOCKADDR_IN stRmtAddr;
    struct in_addr stIPAddr;
    int nAddrSize = sizeof(SOCKADDR);
    SOCKET hSock;
    int nRet;
    
    /* Init local address (to zero) */
    stLclAddr.sin_addr.s_addr = INADDR_ANY;
    
    /* Get the local hostname */
    nRet = gethostname(szLclHost, MAX_HOSTNAME); 
    if (nRet != SOCKET_ERROR) 
    {
      /* Resolve hostname for local address */
      lpstHostent = gethostbyname((LPSTR)szLclHost);
      if (lpstHostent)
        stLclAddr.sin_addr.s_addr = *((u_long FAR*) (lpstHostent->h_addr));
    } 
    
    /* If still not resolved, then try second strategy */
    if (stLclAddr.sin_addr.s_addr == INADDR_ANY) 
    {
      /* Get a UDP socket */
      hSock = socket(AF_INET, SOCK_DGRAM, 0);
      if (hSock != INVALID_SOCKET) 
      {
        /* Connect to arbitrary port and address (NOT loopback) */
        stRmtAddr.sin_family = AF_INET;
        stRmtAddr.sin_port   = htons(IPPORT_ECHO);
        stRmtAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        nRet = connect( hSock, (LPSOCKADDR)&stRmtAddr, sizeof( SOCKADDR ));
        if ( nRet != SOCKET_ERROR ) 
        {
          getsockname( hSock, (LPSOCKADDR)&stLclAddr, (int FAR*)&nAddrSize );
        }
        closesocket( hSock );   
      }
    }
    stIPAddr.s_addr = stLclAddr.sin_addr.s_addr;
    return (inet_ntoa(stIPAddr));
} 

/*****************************************************************************
* passivesock - creates a litening socket to communicate withe a client
*****************************************************************************/
SOCKET passivesock ( char *service, char *protocol ) 
{
    servent *pse;       
    sockaddr_in server; 
    SOCKET s;           

    if ( stricmp( protocol,"tcp" ) == 0 )
        s = socket( AF_INET,SOCK_STREAM,0 );
    else if ( stricmp( protocol,"udp ") == 0 )
        s = socket( AF_INET,SOCK_DGRAM,0 );
    else
        s = INVALID_SOCKET;
    if ( s < 0 || s == INVALID_SOCKET ) 
    {
        cout << "Cannot create socket" << endl;
        return INVALID_SOCKET;
    }

    memset( &server, 0, sizeof( server ));     

    server.sin_family = AF_INET;        
    server.sin_addr.s_addr = INADDR_ANY;    

    if ( pse = getservbyname( service,protocol ))
        server.sin_port = (u_short) pse->s_port;
    else if ( (server.sin_port = htons( (u_short) atoi( service ))) == 0 )
    {
        cout << "Invalid service request" << endl;
        return INVALID_SOCKET;
    }

    if ( bind(s, (LPSOCKADDR) &server, sizeof(SOCKADDR)) < 0 )
    {
        cout << "Cannot bind socket to port: " << service << endl;
        return INVALID_SOCKET;
    }

    if ( stricmp( protocol,"tcp" ) == 0 )
        if ( listen(s,5) < 0 )
        {
            cout << "Cannot listen on port: " << service << endl;
            return INVALID_SOCKET;
        }

    return s;
}

/*****************************************************************************
* wait - function that allows the server to wait a given amount of time before
         closing.
*****************************************************************************/
int wait( SOCKET s, int seconds, int msec ) 
{
    int    stat;                   
    struct timeval stTimeOut;      
    fd_set stReadFDS;               
    fd_set stXcptFDS;               
    
    stTimeOut.tv_sec  = seconds;    
    stTimeOut.tv_usec = msec;       

    FD_ZERO( &stReadFDS );          
    FD_ZERO( &stXcptFDS );          
    FD_SET( s, &stReadFDS );            
    FD_SET( s, &stXcptFDS );            

    // Check to see if Read access is enabled for socket "s"
    stat = select( -1, &stReadFDS, NULL, &stXcptFDS, &stTimeOut );
    if ( stat == SOCKET_ERROR ) 
    {
        std::cout << std::endl << "wait() function failed" << std::endl;
        stat = 0;
    }
    else if ( stat > 0 ) 
    {
        if ( FD_ISSET( s, &stXcptFDS )) 
            stat = 0;
        
        if ( !FD_ISSET( s, &stReadFDS ))   
            stat = 0;
    }

    return stat;
}

/**********************************************
 * main - TCP server for qotd service         *
 **********************************************/
void main (int argc, char *argv[])
{
    char service[MAX_PORT] = "17"; 
    char ip_host[MAX_HOSTNAME]; 
    sockaddr_in clientSockInfo; 
    SOCKET lsocket; 
    SOCKET csocket; 
    int clientLen = sizeof(clientSockInfo); 
    char ip_addr[MAXIPBUF]; 
    char buf[MAX_RECV_BUF]; 
    int len; 
    int stat; 
    bool debug = false;
    bool wait15 = true;
    bool shutcmd = false;

    if (argc > 1)
        strcpy_s(service,MAX_PORT, argv[1]);

    for (int i=2; i<argc; i++)
    {
        if (_stricmp(argv[i],"debug")==0)
        {
            debug = true;
            wait15 = false;
        }

        if (_stricmp(argv[i],"nowait")==0)
            wait15 = false;
    }

    WORD wVersionRequired = 0x0101;
    WSADATA wsaData;
    int err = WSAStartup( wVersionRequired, &wsaData );
    if (err)
    {
        cout << "Unable to initialize Windows Socket library." << endl;
        return;
    }

    lsocket = passivesock(service,"tcp");
    if (lsocket != INVALID_SOCKET)
    {
        strcpy_s(ip_host, MAX_HOSTNAME,GetHostIP());
        cout << timestamp() << " - Started qotd service on " << (string)ip_host << endl;

    while (shutcmd != true) 
    {
        csocket = accept(lsocket,(LPSOCKADDR) &clientSockInfo, &clientLen);

        if (csocket == INVALID_SOCKET)
        cout << "Unable to accept connection request" << endl;

        else
        {
            strcpy_s(ip_addr, MAXIPBUF, inet_ntoa(clientSockInfo.sin_addr) );
            cout << timestamp() << " - Connection accepted from: " << (string)ip_addr << endl;

            stat = 1;
            if (wait15) stat = wait(csocket,WAIT_TIME,0); 
            if (stat > 0)
            {
                len = recv(csocket, buf, MAX_RECV_BUF, 0);
                if (len > 0)
                {
                    if( _stricmp(buf,"hello") == 0 )
                    {
                        cout << timestamp() << " Received command: " << (string)buf <<endl;

                        ifstream file("qotd.txt");
                        while(!file.eof())
                        {
                            file.getline(buf,MAX_RECV_BUF);
                            strcat_s(buf,"\n");
                            send(csocket,buf, strlen(buf), 0);
                        }
                        file.clear();
                        file.close();

                        cout << timestamp() << " - Sent file: qotd.txt "<<endl;
                    }
                    else if( _stricmp(buf,"shutdown") == 0 )
                    {
                        if(_stricmp(ip_addr,ip_host) == 0)
                        {
                            cout << timestamp() << " Received command: " << (string)buf <<endl;
                            cout << timestamp() << "- Service: qotd - will be shutdown" <<endl;
                            shutcmd=true;
                        }
                        else
                            cout << timestamp() << "SHUTDOWN request denied"<<endl;
                    }
                    else
                    {
                        cout << timestamp() << " Received unknown command: " << (string)buf <<endl;
                        cout << timestamp() << " Command ignored"<<endl;
                    }
                }
            }
            shutdown(csocket,1);
            closesocket(csocket);

            cout << timestamp() << " - Connection closed" << endl;
        }
    }
    }
    closesocket(lsocket);
    cout << timestamp() << " - Shutting down server" << endl;
    WSACleanup();
} 

